package com.soccertgc.logging
{
	
	import com.soccertgc.Application;
	import com.soccertgc.config.DebugOptions;
	import com.soccertgc.utils.ErrorUtils;
	import com.soccertgc.utils.StringUtils;
	import com.soccertgc.utils.Utils;
	
	public class Logger
	{
		
		// -------------------------------------------------------------------------------
		// CONSTANTS
		// -------------------------------------------------------------------------------
		
		// Multiline separator
		public static const		MULTILINE_SEPARATOR:String		= "§§§";
		
		// Format text constants 
		public static const		FORMAT_PAD_CHAR:String			= ":";
		public static const		FORMAT_PAD_HEADER:String		= "::: ";
		public static const		FORMAT_PAD_LENGTH:int			= 85;
		
		// -------------------------------------------------------------------------------
		// FIELDS
		// -------------------------------------------------------------------------------
		
		private static var _debugOptions:DebugOptions;
		
		// -------------------------------------------------------------------------------
		// PRIVATE METHODS
		// -------------------------------------------------------------------------------
		
		private static function checkInit():void
		{
			if (!Log.isInit) new Error("Log is not initialized!");
			if (!Logger.isInit) new Error("Logget is not initialized!");
		}
		
		private static function logException(message:String, exception:Error, ... args):void
		{
			// Creates the messages array
			var messages:Array = new Array();
			// Check if a valid Error is provided
			if (exception != null)
			{
				// Opens the error section
				messages.push("::: ERROR :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
				messages.push("");
				messages.push(message);
				messages.push("");
				// Prints the error in the line list (stack and message)
				messages = messages.concat(ErrorUtils.toArray(exception));
				// Closes the error section
				messages.push("");
				messages.push("::: ERROR :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
			}
			else
			{
				// Sets only the message
				messages.push(message);
			}
			// Compose the multiline message (fix to avoid ...args array nestring)
			error.apply(null, [messages.join(Logger.MULTILINE_SEPARATOR)].concat(args));
		}
		
		private static function logFatalException(message:String, exception:Error, ... args):void
		{
			// Creates the messages array
			var messages:Array = new Array();
			// Check if a valid Error is provided
			if (exception != null)
			{
				// Opens the error section
				messages.push("::: FATAL ERROR :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
				messages.push("");
				messages.push(message);
				messages.push("");
				// Prints the error in the line list (stack and message)
				messages = messages.concat(ErrorUtils.toArray(exception));
				// Closes the error section
				messages.push("");
				messages.push("::: FATAL ERROR :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
			}
			else
			{
				// Sets only the message
				messages.push(message);
			}
			// Compose the multiline message (fix to avoid ...args array nestring)
			fatal.apply(null, [messages.join(Logger.MULTILINE_SEPARATOR)].concat(args));
		}

		
		// -------------------------------------------------------------------------------
		// PROPERTIES
		// -------------------------------------------------------------------------------
		
		public static function get isInit():Boolean
		{
			return _debugOptions != null;
		}
		
		// -------------------------------------------------------------------------------
		// PUBLIC METHODS
		// -------------------------------------------------------------------------------
		
		public static function init(debugOptions:DebugOptions, logOptions:LogOptions):void
		{
			// Saves the options
			_debugOptions = debugOptions;
			// Inits the Log
			Log.init(logOptions);
		}
		
		// -------------------------------------------------------------------------------
		// PUBLIC METHODS
		// -------------------------------------------------------------------------------
		
		public static function canDebug(debugKey:String):Boolean
		{
			return _debugOptions.canDebug(debugKey);
		}
		
		/**
		 * 
		 * @param message
		 * @param pad
		 * @return 
		 */		
		public static function format(message:String = "", pad:Boolean = false):String
		{
			var separator:String = StringUtils.isEmpty(message) ? "" : " ";
			return pad	? StringUtils.padRight(FORMAT_PAD_HEADER + message + separator, FORMAT_PAD_CHAR,  FORMAT_PAD_LENGTH) 
						: FORMAT_PAD_HEADER + message;
		}
		
		/**
		 * Signal using a WARN log that a method is not implemented! 
		 */		
		public static function notImplemented(klass:String, method:String):void
		{
			warn("{0}.{1} is not yet implemented!", klass, method);
		}
		
		/**
		 * Dumps the given IDumpable object into all registered with LogLevel.DEBUG  
		 * @param obj Object to dump
		 */		
		public static function dump(obj:IDumpable):void
		{
			// Create dump container array
			var rows:Array = new Array();
			// Retrieve the object dump
			rows = obj.dump(rows);
			// Dump all rows, logging as 
			for (var i:int = 0; i < rows.length; i++)
			{
				debug(rows[i].toString());
			}
		}
		
		public static function info(message:String, ... args):void
		{
			checkInit();
			//Application.instance.logger.info(message, args);
			Log.info(message, Utils.removeRestNesting(args));
		}
		
		public static function debug(message:String, ... args):void
		{
			checkInit();
			// Application.instance.logger.debug.(message, args);
			Log.debug(message, Utils.removeRestNesting(args));
		}
		
		public static function warn(message:String, ... args):void
		{
			checkInit();
			// Application.instance.logger.warn(message, args);
			Log.warn(message, Utils.removeRestNesting(args));
		}
		
		public static function error(message:String, ... args):void
		{
			checkInit();
			// Check if the first argument param is an Error object
			if (args[0] is Error || args[0] == null)
			{
				// Error casting
				var error:Error = args[0] as Error;
				// If the Error is valid, resize the args array removing the first param
				if (error != null) args = args.splice(1, args.length - 1);
				// Multiline log handling with error encoding
				logException(message, error, Utils.removeRestNesting(args));
			}
			else
			{
				// Simple error message logging
				// Application.instance.logger.error(message, args);
				Log.error(message, Utils.removeRestNesting(args));
			}
		}
		
		public static function fatal(message:String, ... args):void
		{
			checkInit();
			// Check if the first argument param is an Error object
			if (args[0] is Error || args[0] == null)
			{
				// Error casting
				var error:Error = args[0] as Error;
				// If the Error is valid, resize the args array removing the first param
				if (error != null) args = args.splice(1, args.length - 1);
				// Multiline log handling with error encoding
				logFatalException(message, error, Utils.removeRestNesting(args));
			}
			else
			{
				// Simple error message logging
				// Application.instance.logger.fatal(message, args);
				Log.fatal(message, Utils.removeRestNesting(args));
			}
		}
		
	}
}